EditableProTable is essentially the same as ProTable, with a few presets added to make it easier to use EditableProTable, turning off the query form and action bar, and modifying value and onChange to make it easy to inherit from antd's Form.
[
{
"id": "1665646530895",
"title": "活动名称0",
"decs": "这个活动真好玩",
"state": "open",
"created_at": "1590486176000"
},
{
"id": "1665646530896",
"title": "活动名称1",
"decs": "这个活动真好玩",
"state": "open",
"created_at": "1590486176000"
},
{
"id": "1665646530897",
"title": "活动名称2",
"decs": "这个活动真好玩",
"state": "open",
"created_at": "1590486176000"
},
{
"id": "1665646530898",
"title": "活动名称3",
"decs": "这个活动真好玩",
"state": "open",
"created_at": "1590486176000"
},
{
"id": "1665646530899",
"title": "活动名称4",
"decs": "这个活动真好玩",
"state": "open",
"created_at": "1590486176000"
},
{
"id": "1665646530900",
"title": "活动名称5",
"decs": "这个活动真好玩",
"state": "open",
"created_at": "1590486176000"
},
{
"id": "1665646530901",
"title": "活动名称6",
"decs": "这个活动真好玩",
"state": "open",
"created_at": "1590486176000"
},
{
"id": "1665646530902",
"title": "活动名称7",
"decs": "这个活动真好玩",
"state": "open",
"created_at": "1590486176000"
},
{
"id": "1665646530903",
"title": "活动名称8",
"decs": "这个活动真好玩",
"state": "open",
"created_at": "1590486176000"
},
{
"id": "1665646530904",
"title": "活动名称9",
"decs": "这个活动真好玩",
"state": "open",
"created_at": "1590486176000"
},
{
"id": "1665646530905",
"title": "活动名称10",
"decs": "这个活动真好玩",
"state": "open",
"created_at": "1590486176000"
},
{
"id": "1665646530906",
"title": "活动名称11",
"decs": "这个活动真好玩",
"state": "open",
"created_at": "1590486176000"
},
{
"id": "1665646530907",
"title": "活动名称12",
"decs": "这个活动真好玩",
"state": "open",
"created_at": "1590486176000"
},
{
"id": "1665646530908",
"title": "活动名称13",
"decs": "这个活动真好玩",
"state": "open",
"created_at": "1590486176000"
},
{
"id": "1665646530909",
"title": "活动名称14",
"decs": "这个活动真好玩",
"state": "open",
"created_at": "1590486176000"
},
{
"id": "1665646530910",
"title": "活动名称15",
"decs": "这个活动真好玩",
"state": "open",
"created_at": "1590486176000"
},
{
"id": "1665646530911",
"title": "活动名称16",
"decs": "这个活动真好玩",
"state": "open",
"created_at": "1590486176000"
},
{
"id": "1665646530912",
"title": "活动名称17",
"decs": "这个活动真好玩",
"state": "open",
"created_at": "1590486176000"
},
{
"id": "1665646530913",
"title": "活动名称18",
"decs": "这个活动真好玩",
"state": "open",
"created_at": "1590486176000"
},
{
"id": "1665646530914",
"title": "活动名称19",
"decs": "这个活动真好玩",
"state": "open",
"created_at": "1590486176000"
}
]| Properties | Description | Type | Default |
|---|---|---|---|
value | Same as dataSource, pass in an array of metadata for table rendering | T[] | undefined |
onChange | The dataSource is triggered when the table is modified, both deletion and modification. | (value:T[])=>void | undefined |
recordCreatorProps | Configuration related to creating a new row of data | RecordCreatorProps & [ButtonProps](https://ant.design/components/button/ #API) | - |
maxLength | The maximum number of rows, the New button will disappear when the maximum number of rows is reached | number | - |
editable | Related configuration of editable table | [TableRowEditable | - |
controlled | Whether controlled, if controlled every edit modifies the dataSource | boolean | false |
editableFormRef | table All forms, with some table-specific operations | React.Ref<EditableFormInstance<T>> | undefined |
Other APIs are the same as ProTable.
When adding a new line, make sure that the recordCreatorProps.record key is unique, otherwise it will cause editing errors.
<EditableTablerowKey="id"recordCreatorProps={{position: position as 'top',// Each time you add a new key, you need itrecord: () => ({ id: getNewId() }),}}/>
Compared with the ProForm form, the editable form adds the following three methods.
/*** Get a row of data* @param rowIndex* @returns T | undefined** @example getRowData(1) can pass in the data of the first row* @example getRowData("id") can also pass in rowKey to get it according to the unique key of your column.*/getRowData?: (rowIndex: string | number) => T | undefined;/*** Get the data of the entire table* @returns T[] | undefined*/getRowsData?: () => T[] | undefined;/*** Setting a row of data will simply merge the data** {title:"old", decs:"old",id:"old"} -> set {title:"new"} -> {title:"new", decs:"old",id:"old" }** @description will only do the merge of the first level object.* {title:"old", decs:{name:"old",key:"old"},id:"old"} -> set {decs:{name:"new"}} -> {title:" old", decs:{name:"new"},id:"old"} -> set {decs:{name:"old"}}** @param rowIndex* @param data* @returns void** Set according to line number* @example setRowData(1, { title:"new" }) You can pass in which row to modify** set according to row id* @example setRowData("id", { title:"new" }) can also pass in rowKey, set it according to the unique key of your column.** Clear the original data* @example setRowData(1, { title:undefined })**/setRowData?: (rowIndex: string | number, data: Partial<T>) => void;
| Property | Description | Type | Default Value |
|---|---|---|---|
| form | Form instance of editable form, use Form.useForm to generate and use | FormInstance | - |
| formProps | form properties can be configured, but onFinish is not supported | `FormProps' | - |
| editableKeys | Row being edited, controlled attributes. The defaultkey will use the configuration of rowKey,if there is no configuration, it will use theindex, it is recommended to use rowKey | Key[] | - |
| onChange | Triggered when row data is modified | (editableKeys: Key[], editableRows: T[]) => void | - |
| onSave | Triggered when a row is saved | (key: Key, row: T,originRow:T,newLine?:newLineConfig) => Promise<any> | - |
| onDelete | Triggered when a row is deleted | (key: Key, row: T) => Promise<any> | - |
| onCancel | Triggered when cancel editing a line | (key: Key, row: T,originRow:T,newLine?:newLineConfig) => Promise<any> | - |
| actionRender | Custom edit mode action bar | (row: T, config: ActionRenderConfig<T>) => ReactNode[] | - |
| deletePopconfirmMessage | The pop-up confirmation box prompt message when deleting | ReactNode | Delete this line? |
| onlyOneLineEditorAlertMessage | Only one line can be edited | ReactNode | Only one line can be edited at the same time |
| onlyAddOneLineAlertMessage | Only one line can be added at the same time | ReactNode | Only add one line |
In order to use it, we preset a New function, which in most cases already meets most new creation needs, but many times the needs are always strange. We have also prepared recordCreatorProps to control the generation of buttons. Same API as the Pro series components, recordCreatorProps={false} turns off the button and uses actionRef.current?.addEditRecord(row) to control the new row.
recordCreatorProps also supports some custom styles, position='top'|'bottom' can be configured to add at the head or at the end of the table. record can be configured to add a new row with default data. Here is an example
recordCreatorProps = {// 顶部添加还是末尾添加position: 'bottom',// 新增一行的方式,默认是缓存,取消后就会消失// 如果设置为 dataSource 会触发 onchange,取消后也不会消失,只能删除newRecordType: 'dataSource',// 不写 key ,会使用 index 当行 idrecord: {},// 按钮的样式设置,可以设置按钮是否显示// 这样可以做最大行限制和最小行限制之类的功能style: {display: 'none',},// https://ant.design/components/button/#API...antButtonProps,};
recordCreatorProps = {// Add at the top or at the endposition: 'bottom',// the way to add a new line, default is cached, will disappear when cancelled// if set to dataSource it will trigger onchange, it won't disappear if cancelled, only deletednewRecordType: 'dataSource',// If you don't write key, index will be used as row idrecord: {},// the style of the button, you can set whether the button is displayed or not// so that you can do things like max row limit and min row limitstyle: {display: 'none',},// https://ant.design/components/button/#API... .antButtonProps,};
As much as we would like the default valueType to meet all our needs, the reality is often not as good as it could be. So we also provide renderFormItem to customize the edit input component.
renderFormItem can be understood as adding an element below Form.
const dom = renderFormItem();<Form.Item>{dom}</Form.Item>;
So as with Form.Item, we assume that the components returned by renderFormItem have value and onChange, and we'll see next that putting a simple TagList component into an editable form with renderFormItem.
Without
valueyou won't be able to inject values and withoutonChangeyou won't be able to modify the row data!
First we define a TagList component.
const TagList: React.FC<{value?: {key: string;label: string;}[];onChange?: (value: {key: string;label: string;}[],) => void;}> = ({ value, onChange }) => {const ref = useRef<Input | null>(null);const [newTags, setNewTags] = useState<{key: string;label: string;}[]>([]);const [inputValue, setInputValue] = useState<string>('');const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {setInputValue(e.target.value);};const handleInputConfirm = () => {let tempsTags = [...(value || [])];if (inputValue && tempsTags.filter((tag) => tag.label === inputValue).length === 0) {tempsTags = [...tempsTags, { key: `new-${tempsTags.length}`, label: inputValue }];}onChange?.(tempsTags);setNewTags([]);setInputValue('');};return (<Space>{(value || []).concat(newTags).map((item) => (<Tag key={item.key}>{item.label}</Tag>))}<Inputref={ref}type="text"size="small"style={{ width: 78 }}value={inputValue}onChange={handleInputChange}onBlur={handleInputConfirm}onPressEnter={handleInputConfirm}/></Space>);};
In the column we can configure it like this.
{title: 'labels',dataIndex: 'labels',width: '40%',renderFormItem: () => <TagList />,render: (_, row) => row?.labels?.map((item) => <Tag key={item.key}>{item.label}</Tag>),},
The effect of the transformed edit form is as follows.
value and onChange are injected automatically, we don't need to inject them explicitly. Data binding is also injected by the edit form itself, and we can get the finished data in onSave. Although we can write complex logic and even web requests inline, we recommend using the split component, which not only provides better performance, but also allows the logic to be split very simply.
renderFormItemis also used to generate query forms, if we need to distinguish between the two cases, we can userenderFormItem: (_, { isEditable }) => (isEditable ? <TagList /> : <Input /> )to render them separately.
If we want to copy a line, or if we only need the Save and Cancel, but not the Delete button, we need to customize it. The editable form provides an API to customize it, and the following will show the code directly:
render: (text, record, _, action) => [<akey="editable"onClick={() => {action?.startEditable? (record.id);}}>Edit</a>,<EditableProTable.RecordCreatorrecord={()=>{...record,id: (Math.random() * 1000000).toFixed(0),}}><a>Copy this row to the end</a</EditableProTable.RecordCreator>,];
const editable = {actionRender: (row, config, defaultDom) => [defaultDom.save, defaultDom.cancel],};